Step 2: import data and train-test split
#train-test split
info <- read.csv(train_label_path)
n <- nrow(info) #get number of rows from csv
n_train <- round(n*(4/5), 0) #use 4/5 amount of data for training
train_idx <- sample(info$Index, n_train, replace = F) #grab indexes used for training
test_idx <- setdiff(info$Index, train_idx) # get indexes not used for training
Fiducial points are stored in matlab format. In this step, we read them and store them in a list.
#function to read fiducial points
#input: index
#output: matrix of fiducial points corresponding to the index
n_files <- length(list.files(train_image_dir,'*jpg'))
readMat.matrix <- function(index){
return(round(readMat(paste0(train_pt_dir, sprintf("%04d", index), ".mat"))[[1]],0))
}
#load fiducial points
fiducial_pt_list <- lapply(1:n_files, readMat.matrix)
save(fiducial_pt_list, file="../output/fiducial_pt_list.RData")
Advanced Models:
Create weight test
label_test <- as.integer(dat_test$label)
weight_test <- rep(NA, length(label_test))
for (i in unique(label_test)){
weight_test[label_test == i] = 0.5 * length(label_test) / length(label_test[label_test == i])
}
# training weights
weight_train <- rep(NA, length(label_train))
for (v in unique(label_train)){
weight_train[label_train == v] = 0.5 * length(label_train) / length(label_train[label_train == v])
}
if (run.gbm){
if (sample.reweight){
tm_train <- system.time(fit_train <- train_gbm(dat_train, s=0.1, K=K, n=gbm.numtrees,w = weight_train))
} else {
tm_train <- system.time(fit_train <- train_gbm(dat_train, s=0.1, K=K, n=gbm.numtrees,w = NULL))
}
# plot the performance
best.iter.oob <- gbm.perf(fit_train,method="OOB") # returns out-of-bag estimated best number of trees
print(best.iter.oob)
best.iter.cv <- gbm.perf(fit_train,method="cv") # returns K-fold cv estimate of best number of trees
print(best.iter.cv)
} else {
if (sample.reweight){
tm_train <- system.time(fit_train <- train(feature_train, label_train, w = weight_train, par_best))
} else {
tm_train <- system.time(fit_train <- train(feature_train, label_train, w = NULL, par_best))
}
}
save(fit_train, file="../output/fit_train.RData")
Step 5: Run test on test images
tm_test = NA
feature_test <- as.matrix(dat_test[, 1:ncol(dat_test)-1])
if(run.test){
load(file="../output/fit_train.RData")
if (run.gbm){
tm_test <- system.time(prob_pred<-test_gbm(fit_train,as.data.frame(feature_test),n=best.iter.cv,pred.type = 'response'))
label_pred <- colnames(prob_pred)[apply(prob_pred, 1, which.max)]
} else {
tm_test <- system.time({label_pred <- as.integer(test(fit_train, feature_test, pred.type = 'class'));
prob_pred <- test(fit_train, feature_test, pred.type = 'response')})
}
}
Random Forest:
Rebalance Train Set
if(run.rf){
# transfer label column from factor to numeric
dat_train$label <- as.numeric(dat_train$label)-1
dat_test$label <- as.numeric(dat_test$label)-1
#Rebalancing training data-Bootstrap Random Over-Sampling Examples Technique (ROSE) source
if(run.balanced.data){
dat_train_balanced_rose<-ROSE(label~., dat_train,seed=2020)$data
save(dat_train_balanced_rose, file="../output/balanced_data.RData")
} else {
load(file="../output/balanced_data.RData")
}
table(dat_train_balanced_rose$label)
}
Tune RF
if(run.rf){
source("../lib/random_forest.R")
if(tune.random.forest){
time.rf.tune <- system.time(rf.tune <- random_forest_tune(dat_train_balanced_rose))
save(rf.tune, file="../output/rf_tune.RData")
}else(
load("../output/rf_tune.RData")
)
rf.tune
}
mtry = 154 is the best.
Find the best ntrees
if(run.rf){
source("../lib/random_forest.R")
#Train 500
if(tune.random.forest){
time.rf.train <- system.time(random_forest_fit_500 <- random_forest_train_500(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit_500, file = "../output/rf_train_500_trees.RData")
}
#Test 500
random_forest_test_prep=NA
if(tune.random.forest){
load(file="../output/rf_train_500_trees.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit_500,testset = dat_test)
)
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
random_forest_label<-round(random_forest_test_prep)
accu_rf <- sum(weight_test * (random_forest_label == label_test)) / sum(weight_test)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on imbalanced testing data", "is", accu_rf_test*100, "%.\n")
cat("The accuracy of model: Random Forest on balanced testing data", "is", accu_rf*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
}
# The AUC of model after reweighting: RF is 0.5031999 .
# The accuracy of model: Random Forest on imbalanced testing data is 80.33333 %.
# The accuracy of model: Random Forest on balanced testing data is 50.31999 %.
# Time for training model Random Forest = 20.95 s
# Time for testing model Random Forest = 0.09 s
#Train 1000
if(tune.random.forest){
time.rf.train <- system.time(random_forest_fit_1000 <- random_forest_train_1000(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit_1000, file = "../output/rf_train_1000_trees.RData")
}
#Test 1000
random_forest_test_prep=NA
if(tune.random.forest){
load(file="../output/rf_train_1000_trees.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit_1000,testset = dat_test)
)
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
random_forest_label<-round(random_forest_test_prep)
accu_rf <- sum(weight_test * (random_forest_label == label_test)) / sum(weight_test)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on imbalanced testing data", "is", accu_rf_test*100, "%.\n")
cat("The accuracy of model: Random Forest on balanced testing data", "is", accu_rf*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
}
#Train 1500
if(tune.random.forest){
time.rf.train <- system.time(random_forest_fit_1500 <- random_forest_train_1500(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit_1500, file = "../output/rf_train_1500_trees.RData")
}
#Test 1500
random_forest_test_prep=NA
if(tune.random.forest){
load(file="../output/rf_train_1500_trees.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit_1500,testset = dat_test)
)
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
random_forest_label<-round(random_forest_test_prep)
accu_rf <- sum(weight_test * (random_forest_label == label_test)) / sum(weight_test)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on imbalanced testing data", "is", accu_rf_test*100, "%.\n")
cat("The accuracy of model: Random Forest on balanced testing data", "is", accu_rf*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
}
#Train 2000
if(tune.random.forest){
time.rf.train <- system.time(random_forest_fit_2000 <- random_forest_train_2000(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit_2000, file = "../output/rf_train_2000_trees.RData")
}
#Test 2000
random_forest_test_prep=NA
if(tune.random.forest){
load(file="../output/rf_train_2000_trees.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit_2000,testset = dat_test)
)
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
random_forest_label<-round(random_forest_test_prep)
accu_rf <- sum(weight_test * (random_forest_label == label_test)) / sum(weight_test)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on imbalanced testing data", "is", accu_rf_test*100, "%.\n")
cat("The accuracy of model: Random Forest on balanced testing data", "is", accu_rf*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
}
#Train 2500
if(tune.random.forest){
time.rf.train <- system.time(random_forest_fit_2500 <- random_forest_train_2500(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit_2500, file = "../output/rf_train_2500_trees.RData")
}
#Test 2500
random_forest_test_prep=NA
if(tune.random.forest){
load(file="../output/rf_train_2500_trees.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit_2500,testset = dat_test)
)
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
random_forest_label<-round(random_forest_test_prep)
accu_rf <- sum(weight_test * (random_forest_label == label_test)) / sum(weight_test)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on imbalanced testing data", "is", accu_rf_test*100, "%.\n")
cat("The accuracy of model: Random Forest on balanced testing data", "is", accu_rf*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
}
}
Testing Result: When trees = 500: The AUC of model after reweighting: RF is 0.5116745 . The accuracy of model: Random Forest on imbalanced testing data is 80.66667 %. The accuracy of model: Random Forest on balanced testing data is 51.16745 %. Time for training model Random Forest = 713.63 s Time for testing model Random Forest = 0.19 s
When trees = 1000 The AUC of model after reweighting: RF is 0.5201491 . The accuracy of model: Random Forest on imbalanced testing data is 81 %. The accuracy of model: Random Forest on balanced testing data is 52.01491 %. Time for training model Random Forest = 1367.94 s Time for testing model Random Forest = 0.28 s
When trees = 1500 The AUC of model after reweighting: RF is 0.5201491 . The accuracy of model: Random Forest on imbalanced testing data is 81 %. The accuracy of model: Random Forest on balanced testing data is 52.01491 %. Time for training model Random Forest = 2077.56 s Time for testing model Random Forest = 0.36 s
When trees = 2000 The AUC of model after reweighting: RF is 0.5201491 . The accuracy of model: Random Forest on imbalanced testing data is 81 %. The accuracy of model: Random Forest on balanced testing data is 52.01491 %. Time for training model Random Forest = 3142.77 s Time for testing model Random Forest = 0.56 s
When trees = 2500 The AUC of model after reweighting: RF is 0.5159118 . The accuracy of model: Random Forest on imbalanced testing data is 80.83333 %. The accuracy of model: Random Forest on balanced testing data is 51.59118 %. Time for training model Random Forest = 3963.67 s Time for testing model Random Forest = 0.62 s
Therefore, we should use trees = 1000.
Train RF with tuning parameters
if(run.rf){
source("../lib/random_forest.R")
if(train.random.forest){
time.rf.train <- system.time(random_forest_fit <- random_forest_train(dat_train_balanced_rose,mtry = 154))
save(random_forest_fit, file = "../output/random_forest_train.RData")
save(time.rf.train,file = "../output/random_forest_train_time.RData")
}else{
load(file = "../output/random_forest_train_time.RData")
load(file = "../output/random_forest_train.RData")
}
}
Test RF with tuning parameters
if(run.rf){
random_forest_test_prep=NA
if(run.test){
load(file="../output/random_forest_train.RData")
time.rf.test <- system.time(
random_forest_test_prep <- random_forest_test(
model = random_forest_fit,testset = dat_test)
)
}
## reweight the test data to represent a balanced label distribution
if (run.gbm){
accu <- mean(dat_test$label == label_pred)
cat("The accuracy of GBM baseline model is", mean(dat_test$label == label_pred)*100, "%.\n")
} else {
label_test <- as.integer(dat_test$label)
weight_test <- rep(NA, length(label_test))
for (v in unique(label_test)){
weight_test[label_test == v] = 0.5 * length(label_test) / length(label_test[label_test == v])
}
accu <- sum(weight_test * (label_pred == label_test)) / sum(weight_test)
tpr.fpr <- WeightedROC(prob_pred, label_test, weight_test)
auc <- WeightedAUC(tpr.fpr)
cat("The accuracy of model:", model_labels[which.min(res_cv$mean_error)], "is", accu*100, "%.\n")
cat("The AUC of model:", model_labels[which.min(res_cv$mean_error)], "is", auc, ".\n")
}
random_forest_test_prep <- as.numeric(as.character(random_forest_test_prep))
accu_rf_test <- mean(random_forest_test_prep == dat_test$label)
}
Calculate weightedAUC on testing split
if(run.rf){
random_forest_label<-round(random_forest_test_prep)
#prob_pred <- lable_pred
tpr.fpr <- WeightedROC(random_forest_test_prep, label_test, weight_test)
auc_rf <- WeightedAUC(tpr.fpr)
}
Summary of RF
if(run.rf){
cat("The AUC of model after reweighting: RF", "is", auc_rf, ".\n")
cat("The accuracy of model: Random Forest on testing data", "is", accu_rf_test*100, "%.\n")
cat("Time for training model Random Forest = ", time.rf.train[1], "s \n")
cat("Time for testing model Random Forest = ",time.rf.test[1], "s \n")
#label_test
cat("The accuracy of model:", model_labels[which.min(res_cv$mean_error)], "is", accu*100, "%.\n")
cat("The AUC of model:", model_labels[which.min(res_cv$mean_error)], "is", auc, ".\n")
}
Summarize Running Time
Prediction performance matters, so does the running times for constructing features and for training the model, especially when the computation resource is limited.
# cat("Time for constructing training features=", tm_feature_train[1], "s \n")
# cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
# cat("Time for training model=", tm_train[1], "s \n")
# cat("Time for testing model=", tm_test[1], "s \n")
SVM Model
- Balance the Training Data
if(run.svm){
tm_svm_rebalanced_train <- NA
if(needs.balanced){
tm_svm_rebalanced_train <- system.time(svm_training_data <- ROSE(label ~ ., data = dat_train)$data)
save(svm_training_data, file="../output/svm_training_data.RData")
save(tm_svm_rebalanced_train, file="../output/tm_svm_rebalanced_train.RData")
} else {
svm_training_data <- dat_train
tm_svm_rebalanced_train <- tm_feature_train
}
} else {
load(file="../output/tm_svm_rebalanced_train.RData")
}
if(run.svm){
tm_svm_linear_mod <- NA
tm_svm_radial_mod <- NA
if(model.selection){
svm_model_auc <- rep(NA, 2)
### linear kernel
if(run.cv){
#best.linear.cost <- svm_linear_cost_tune(svm_training_data)
#cat("The best cost for svm model with linear kernel is: ", best.linear.cost$best.parameters$cost)
tm_svm_linear_mod <- system.time(svm_linear_mod <- svm_linear_train(svm_training_data, 0.01, K))
save(svm_linear_mod, file="../output/svm_linear_mod.RData")
save(tm_svm_linear_mod, file="../output/tm_svm_linear_mod.RData")
} else {
load(file="../output/svm_linear_mod.RData")
load(file="../output/tm_svm_linear_mod.RData")
}
svm_linear_pred <- svm_test(svm_linear_mod, svm_training_data, TRUE)
#mean(round(svm_linear_pred == svm_training_data$label))
svm_linear_accu <- mean(round(svm_linear_pred == svm_training_data$label))
tpr.fpr_linear <- WeightedROC(as.numeric(svm_linear_pred), svm_training_data$label)
svm_model_auc[1] <- WeightedAUC(tpr.fpr_linear)
### radial basis kernel
if(run.cv){
#best.radial.cost <- svm_radial_cost_tune(svm_training_data)
#radial_cost = best.radial.cost$best.parameters$cost
#radial_gamma = best.radial.cost$best.parameters$gamma
tm_svm_radial_mod < system.time(svm_radial_mod <- svm_radial_train(svm_training_data, 1, K))
save(svm_radial_mod, file="../output/svm_radial_mod.RData")
save(tm_svm_radial_mod, file="../output/tm_svm_radial_mod.RData")
} else {
load(file="../output/svm_radial_mod.RData")
load(file="../output/tm_svm_radial_mod.RData")
}
svm_radial_pred <- svm_test(svm_radial_mod, svm_training_data, TRUE)
# evaluate performance
svm_radial_accu <- mean(round(svm_radial_pred == svm_training_data$label))
tpr.fpr_default <- WeightedROC(as.numeric(svm_radial_pred), svm_training_data$label)
svm_model_auc[2] <- WeightedAUC(tpr.fpr_default)
} else {
load(file="../output/svm_linear_mod.RData")
load(file="../output/tm_svm_linear_mod.RData")
load(file="../output/svm_radial_mod.RData")
load(file="../output/tm_svm_radial_mod.RData")
}
### Evaluation on Testing Data
tm_svm_rebalanced_test <- NA
if(needs.balanced){
tm_svm_rebalanced_test <- system.time(svm_testing_data <- ROSE(label ~ ., data = dat_test)$data)
save(svm_testing_data, file="../output/svm_testing_data.RData")
save(tm_svm_rebalanced_test, file="../output/tm_svm_rebalanced_test.RData")
} else {
svm_testing_data <- dat_test
tm_svm_rebalanced_test <- tm_feature_test
}
if(run.svm.test){
svm_auc <- rep(NA, 2)
svm_accu <- rep(NA, 2)
## linear
tm_svm_linear_test <- system.time(svm_linear_pred <- svm_test(svm_linear_mod, svm_testing_data))
svm_accu[1] = mean(round(svm_linear_pred == svm_testing_data$label))
tpr.fpr.linear <- WeightedROC(as.numeric(svm_linear_pred), svm_testing_data$label)
svm_auc[1] = WeightedAUC(tpr.fpr.linear)
## rbf
tm_svm_rbf_test <- system.time(svm_rbf_pred <- svm_test(svm_radial_mod, svm_testing_data))
svm_accu[2] = mean(round(svm_rbf_pred == svm_testing_data$label))
tpr.fpr.rbf <- WeightedROC(as.numeric(svm_linear_pred), svm_testing_data$label)
svm_auc[2] = WeightedAUC(tpr.fpr.rbf)
save(tm_svm_radial_mod, file="../output/tm_svm_linear_test.RData")
## performance
svm_auc
svm_accu
cat("The accuracy of svm model is", svm_accu[2]*100, "%.\n")
cat("The AUC of svm model is", svm_auc[2], ".\n")
} else {
load(file="../output/tm_svm_rebalanced_test.RData")
}
} else {
load(file="../output/svm_radial_mod.RData")
load(file="../output/tm_svm_radial_mod.RData")
}
if(run.svm){
#cat("Time for rebalancing training data =", tm_svm_rebalanced_train[1], "s \n")
#cat("Time for rebalancing testing data =", tm_svm_rebalanced_test[1], "s \n")
#cat("Time for training svm model =", tm_svm_radial_mod[1], "s \n")
cat("Time for testing svm model=", tm_svm_rbf_test[1], "s \n")
}
Ridge Model
apply constructed ridge model to the training data
tm_ridge_train <- NA
if (train.ridge){
dat_train_rebalanced <- ROSE(label ~ ., data = dat_train, seed=2021)$data
tm_ridge_train <- system.time(ridge_cv_model<-ridge_train(train_data=dat_train_rebalanced, alpha=alpha, K=K, lambda=lambda))
save(ridge_cv_model, file="../output/ridge_cv_model.RData")
save(tm_ridge_train, file="../output/ridge_train_time.RData")
}else{
load(file="../output/ridge_cv_model.RData")
load(file="../output/ridge_train_time.RData")
}
use cross-validation to choose the optimal lambda with smallest MSE
if (run.cv){
set.seed(2020)
feature_train = as.matrix(dat_train[, -6007])
label_train = as.integer(dat_train$label)
ridge_model = cv.glmnet(x=feature_train, y=label_train, alpha=alpha, nfolds=K, lambda=lambda)
opt_lambda = ridge_model$lambda.min
save(opt_lambda, file="../output/ridge_optimal_lambda.RData")
}else{
load(file="../output/ridge_optimal_lambda.RData")
}
predict testing data with the optimal lambda
tm_ridge_test = NA
if(run.test){
load("../output/ridge_cv_model.RData")
feature_test <- as.matrix(dat_test[, -6007])
tm_ridge_test <- system.time(label_pred<-as.integer(ridge_test(model=ridge_cv_model, features=feature_test, pred.type = 'class')))
save(tm_ridge_test, file="../output/ridge_test_time.RData")
} else{
load(file="../output/ridge_test_time.RData")
}
summarize running time
cat("Time for constructing training features=", tm_feature_train[1], "s \n")
cat("Time for constructing testing features=", tm_feature_test[1], "s \n")
cat("Time for training ridge model=", tm_ridge_train[1], "s \n")
cat("Time for testing ridge model=", tm_ridge_test[1], "s \n")
run evaluation on independent testing data
load("../output/ridge_cv_model.RData")
feature_test <- as.matrix(dat_test[, -6007])
label_pred = as.integer(predict(ridge_cv_model, s=opt_lambda, newx=feature_test, type='class'))
label_test = as.integer(dat_test$label)
compare <- cbind (label_test, label_pred)
ridge_accuracy = mean(apply(compare, 1, min)/apply(compare, 1, max))
cat("The accuracy of the ridge model is", ridge_accuracy*100, "%.\n")
ridge_AUC = auc(roc(label_pred,label_test))
cat("The AUC of the ridge model is", ridge_AUC, ".\n")
PCA + LDA
if(run.pca_lda){
if(needs.balanced){
balanced_train_data <- ROSE(label~.,data = dat_train)$data
save(balanced_train_data, file="../output/feature_balanced_train.RData")
} else {
load(balanced_train_data, file="../output/feature_train.RData")
}
### test
if(run.pca_lad.test){
if(needs.balanced){
balanced_test_data <- ROSE(label~.,data = dat_test)$data
save(balanced_test_data, file="../output/feature_balanced_test.RData")
} else {
load(balanced_test_data, file="../output/feature_balanced_test.RData")
}
}
}
###Reference - Du, S., Tao, Y., & Martinez, A. M. (2014). Compound facial expressions of emotion. Proceedings of the National Academy of Sciences, 111(15), E1454-E1462.
LS0tCnRpdGxlOiAiUHJvamVjdCAzOiBGYWNpYWwgRXhwcmVzc2lvbiBQcmVkaWN0aXZlIE1vZGVsaW5nIgphdXRob3I6ICJKaW5nYmluIENhbywgQ2h1YW5jaHVhbiBMaXUsIERlbm5pcyBTaHBpdHMsIFlpbmd5YW8gV3UsIFppa3VuIFpodWFuZyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KI1Rlc3QgQnJhbmNoIGNyZWF0ZWQKaWYoIXJlcXVpcmUoIlIubWF0bGFiIikpewogIGluc3RhbGwucGFja2FnZXMoIlIubWF0bGFiIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImRwbHlyIikpewogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KaWYoIXJlcXVpcmUoImdncGxvdDIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCn0KaWYoIXJlcXVpcmUoImNhcmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKfQppZighcmVxdWlyZSgiZ2xtbmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpCn0KaWYoIXJlcXVpcmUoIldlaWdodGVkUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoIldlaWdodGVkUk9DIikKfQppZighcmVxdWlyZSgiZ2JtIikpewogIGluc3RhbGwucGFja2FnZXMoImdibSIpCn0KaWYoIXJlcXVpcmUoIkRNd1IiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiRE13UiIpCn0KaWYoIXJlcXVpcmUoIk9wZW5JbWFnZVIiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJPcGVuSW1hZ2VSIikKfQppZighcmVxdWlyZSgiQVVDIikpewogaW5zdGFsbC5wYWNrYWdlcygiQVVDIikKfQppZighcmVxdWlyZSgiZTEwNzEiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJlMTA3MSIpCn0KaWYoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKXsKIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpCn0KaWYoIXJlcXVpcmUoInhnYm9vc3QiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ4Z2Jvb3N0IikKfQppZighcmVxdWlyZSgidGliYmxlIikpewogaW5zdGFsbC5wYWNrYWdlcygidGliYmxlIikKfQppZighcmVxdWlyZSgiUk9TRSIpKXsKIGluc3RhbGwucGFja2FnZXMoIlJPU0UiKQp9CmlmKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSl7CiBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQp9CmlmKCFyZXF1aXJlKCJjYVRvb2xzIikpewogIGluc3RhbGwucGFja2FnZXMoImNhVG9vbHMiKQp9CmlmKCFyZXF1aXJlKCJwcmVkaWN0aW9uIikpewogIGluc3RhbGwucGFja2FnZXMoInByZWRpY3Rpb24iKQp9CmlmKCFyZXF1aXJlKCJwUk9DIikpewogIGluc3RhbGwucGFja2FnZXMoInBST0MiKQp9CgpsaWJyYXJ5KFIubWF0bGFiKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShXZWlnaHRlZFJPQykKbGlicmFyeShnYm0pCmxpYnJhcnkoRE13UikKIyMjIG5ldyBsaWJyYXJpZXMKbGlicmFyeShPcGVuSW1hZ2VSKQpsaWJyYXJ5KEFVQykKbGlicmFyeShlMTA3MSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoUk9TRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShwcmVkaWN0aW9uKQpsaWJyYXJ5KHBST0MpCmBgYAoKCiMjIyBTdGVwIDAgc2V0IHdvcmsgZGlyZWN0b3JpZXMKYGBge3Igd2tkaXIsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDIwMjApCmBgYAoKUHJvdmlkZSBkaXJlY3RvcmllcyBmb3IgdHJhaW5pbmcgaW1hZ2VzLiBUcmFpbmluZyBpbWFnZXMgYW5kIFRyYWluaW5nIGZpZHVjaWFsIHBvaW50cyB3aWxsIGJlIGluIGRpZmZlcmVudCBzdWJmb2xkZXJzLiAKYGBge3J9CnRyYWluX2RpciA8LSAiLi4vZGF0YS90cmFpbl9zZXQvIiAjIFRoaXMgd2lsbCBiZSBtb2RpZmllZCBmb3IgZGlmZmVyZW50IGRhdGEgc2V0cy4KdHJhaW5faW1hZ2VfZGlyIDwtIHBhc3RlKHRyYWluX2RpciwgImltYWdlcy8iLCBzZXA9IiIpCnRyYWluX3B0X2RpciA8LSBwYXN0ZSh0cmFpbl9kaXIsICAicG9pbnRzLyIsIHNlcD0iIikKdHJhaW5fbGFiZWxfcGF0aCA8LSBwYXN0ZSh0cmFpbl9kaXIsICJsYWJlbC5jc3YiLCBzZXA9IiIpCmBgYAoKIyMjIFN0ZXAgMTogc2V0IHVwIGNvbnRyb2xzIGZvciBldmFsdWF0aW9uIGV4cGVyaW1lbnRzLgoKSW4gdGhpcyBjaHVuaywgd2UgaGF2ZSBhIHNldCBvZiBjb250cm9scyBmb3IgdGhlIGV2YWx1YXRpb24gZXhwZXJpbWVudHMuIAoKKyAoVC9GKSBjcm9zcy12YWxpZGF0aW9uIG9uIHRoZSB0cmFpbmluZyBzZXQKKyAoVC9GKSByZXdlaWdodGluZyB0aGUgc2FtcGxlcyBmb3IgdHJhaW5pbmcgc2V0IAorIChudW1iZXIpIEssIHRoZSBudW1iZXIgb2YgQ1YgZm9sZHMKKyAoVC9GKSBwcm9jZXNzIGZlYXR1cmVzIGZvciB0cmFpbmluZyBzZXQKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiBhbiBpbmRlcGVuZGVudCB0ZXN0IHNldAorIChUL0YpIHByb2Nlc3MgZmVhdHVyZXMgZm9yIHRlc3Qgc2V0CgorIChUL0YpIHJ1biBpbXByb3ZlZCBnYm0gbW9kZWwKKyAobnVtYmVyKSBnYm0ubnVtdHJlZXMsIHRoZSBudW1iZXIgb2YgdHJlZXMgdG8gdXNlIGluIEdCTSBiYXNlbGluZQorIChUL0YpIHJldHVybiBwb2x5bm9taWFsIGZlYXR1cmVzIG1hdHJpeCBvbmx5CisgKFQvRikgYWRkIHBvbHlub21pYWwgZmVhdHVyZXMgdG8gc3RhcnRlciBjb2RlIGZlYXR1cmVzIG1hdHJpeAoKKyAoVC9GKSBydW4gc3ZtIG1vZGVsCisgKFQvRikgcmViYWxhbmNlIHRyYWluaW5nIHNldAorIChUL0YpIHBlcmZvcm0gbW9kZWwgc2VsZWN0aW9uIG92ZXIgYSBsaXN0IG9mIHN2bSBtb2RlbHMKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCisgKFQvRikgcnVuIHJhbmRvbSBmb3Jlc3QgbW9kZWwKKyAoVC9GKSByZWJhbGFuY2UgdHJhaW5pbmcgc2V0CisgKFQvRikgdHJhaW4gcmFuZG9tIGZvcmVzdCBtb2RlbAorIChUL0YpIHR1bmUgaHlwZXJwYXJhbWV0ZXJzIGZvciByYW5kb20gZm9yZXN0IG1vZGVsCgorIChUL0YpIHJ1biByaWRnZSBtb2RlbAorICgwLzEpIGFscGhhLCBhbHBoYT0wIGZvciByaWRnZSByZWdyZXNzaW9uLCBhbHBoYT0xIGZvciBsYXNzbyByZWdyZXNzaW9uCisgKFQvRikgdHJhaW4gcmlkZ2UgbW9kZWwKCisgKFQvRikgcnVuIFBDQStMREEgbW9kZWwKKyAoVC9GKSBydW4gZGlmZmVyZW50IHByaW5jaXBhbCBjb21wb25lbnRzCisgKFQvRikgcnVuIExEQSBvbiB0cmFpbmluZyBzZXQKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiB0aGUgdGVzdCBzZXQKCmBgYHtyIGV4cF9zZXR1cH0KcnVuLmN2IDwtIFRSVUUgIyBydW4gY3Jvc3MtdmFsaWRhdGlvbiBvbiB0aGUgdHJhaW5pbmcgc2V0CnNhbXBsZS5yZXdlaWdodCA8LSBGQUxTRSAjIHJ1biBzYW1wbGUgcmV3ZWlnaHRpbmcgaW4gbW9kZWwgdHJhaW5pbmcKSyA8LSA1ICAjIG51bWJlciBvZiBDViBmb2xkcwpydW4uZmVhdHVyZS50cmFpbiA8LSBUUlVFICMgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdHJhaW5pbmcgc2V0CnJ1bi50ZXN0IDwtIFRSVUUgIyBydW4gZXZhbHVhdGlvbiBvbiBhbiBpbmRlcGVuZGVudCB0ZXN0IHNldApydW4uZmVhdHVyZS50ZXN0IDwtIFRSVUUgIyBwcm9jZXNzIGZlYXR1cmVzIGZvciB0ZXN0IHNldAoKIyBnYm0KcnVuLmdibSA8LSBGQUxTRSAjIGdibShpbXJvdmVkKSBpcyB0aGUgY2hvc2VuIGFkdmFuY2VkIG1vZGVsCmdibS5udW10cmVlcyA8LSAxMDAwICNudW1iZXIgb2YgdHJlZXMgdG8gdXNlIGluIGdibQpydW4ucG9seS5mZWF0dXJlIDwtIFRSVUUgIyBwcm9jZXNzIHBvbHkgZmVhdHVyZXMKcnVuLmFkZC5wb2x5LmZlYXR1cmUgPC0gVFJVRSAjIGFuZCBwb2x5IGZlYXR1cmVzIHRvIGRpc3QgbWF0cml4CgojIHN2bQpydW4uc3ZtIDwtIEZBTFNFICMgc3ZtIGlzIHRoZSBjaG9zZW4gYWR2YW5jZWQgbW9kZWwKbmVlZHMuYmFsYW5jZWQgPC0gVFJVRSAjIGJhbGFuY2UgZGF0YSBmb3IgbW9kZWwgZml0dGluZwptb2RlbC5zZWxlY3Rpb24gPC0gVFJVRSAjIHBlcmZvcm0gbW9kZWwgc2VsZWN0aW9uIG9uIHN2bSBtb2RlbHMKcnVuLnN2bS50ZXN0IDwtIFRSVUUgIyBldmFsdWF0ZSBwZXJmb3JtYW5jZSBvbiB0aGUgdGVzdCBzZXQKCiMgcmFuZG9tIGZvcmVzdApydW4ucmYgPCBGQUxTRSAjIHJhbmRvbSBmb3Jlc3QgaXMgdGhlIGNob3NlbiBhZHZhbmNlZCBtb2RlbApydW4uYmFsYW5jZWQuZGF0YSA8LSBUUlVFICMgd2hldGhlciBvciBub3QgYmFsYW5jZSB0aGUgZGF0YQp0cmFpbi5yYW5kb20uZm9yZXN0IDwtIEZBTFNFICMgdHJhaW4gUmFuZG9tIEZvcmVzdCBNb2RlbAp0dW5lLnJhbmRvbS5mb3Jlc3QgPC0gRkFMU0UgIyB0dW5lIFJhbmRvbSBGb3Jlc3QgTW9kZWwKCiMgcmlkZ2UKcnVuLnJpZGdlIDwtIEZBTFNFICMgcmlkZ2UgaXMgdGhlIGNob3NlbiBhZHZhbmNlZCBtb2RlbAphbHBoYSA8LSAwICMgcmlkZ2UgcmVncmVzc2lvbgp0cmFpbi5yaWRnZSA8LSBUUlVFICMgdHJhaW4gcmlkZ2UgbW9kZWwKCiMgUENBICsgTERBCnJ1bi5wY2FfbGRhIDwtIEZBTFNFICMgUENBICsgTERBIGlzIHRoZSBjaG9zZW4gYWRjYW5jZWQgbW9kZWwKcnVuLnNlbGVjdF9QQyA8LSBUUlVFICNydW4gZGlmZmVyZW50IFBDcwpydW4ubGRhIDwtIFRSVUUgIyBydW4gbGRhIG9uIHRoZSB0cmFpbmluZyBzZXQKcnVuLnBjYV9sYWQudGVzdCA8LSBUUlVFICMgZXZhbHVhdGUgcGVyZm9ybWFuY2Ugb24gdGhlIHRlc3Qgc2V0CmBgYAoKVXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiBvciBpbmRlcGVuZGVudCB0ZXN0IHNldCBldmFsdWF0aW9uLCB3ZSBjb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiBtb2RlbHMgd2l0aCBkaWZmZXJlbnQgc3BlY2lmaWNhdGlvbnMuIAoKIyMjIFN0ZXAgMjogaW1wb3J0IGRhdGEgYW5kIHRyYWluLXRlc3Qgc3BsaXQgCmBgYHtyfQojdHJhaW4tdGVzdCBzcGxpdAppbmZvIDwtIHJlYWQuY3N2KHRyYWluX2xhYmVsX3BhdGgpCm4gPC0gbnJvdyhpbmZvKSAjZ2V0IG51bWJlciBvZiByb3dzIGZyb20gY3N2Cm5fdHJhaW4gPC0gcm91bmQobiooNC81KSwgMCkgI3VzZSA0LzUgYW1vdW50IG9mIGRhdGEgZm9yIHRyYWluaW5nCnRyYWluX2lkeCA8LSBzYW1wbGUoaW5mbyRJbmRleCwgbl90cmFpbiwgcmVwbGFjZSA9IEYpICNncmFiIGluZGV4ZXMgdXNlZCBmb3IgdHJhaW5pbmcKdGVzdF9pZHggPC0gc2V0ZGlmZihpbmZvJEluZGV4LCB0cmFpbl9pZHgpICMgZ2V0IGluZGV4ZXMgbm90IHVzZWQgZm9yIHRyYWluaW5nCmBgYAoKRmlkdWNpYWwgcG9pbnRzIGFyZSBzdG9yZWQgaW4gbWF0bGFiIGZvcm1hdC4gSW4gdGhpcyBzdGVwLCB3ZSByZWFkIHRoZW0gYW5kIHN0b3JlIHRoZW0gaW4gYSBsaXN0LgpgYGB7ciByZWFkIGZpZHVjaWFsIHBvaW50c30KI2Z1bmN0aW9uIHRvIHJlYWQgZmlkdWNpYWwgcG9pbnRzCiNpbnB1dDogaW5kZXgKI291dHB1dDogbWF0cml4IG9mIGZpZHVjaWFsIHBvaW50cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBpbmRleApuX2ZpbGVzIDwtIGxlbmd0aChsaXN0LmZpbGVzKHRyYWluX2ltYWdlX2RpciwnKmpwZycpKQpyZWFkTWF0Lm1hdHJpeCA8LSBmdW5jdGlvbihpbmRleCl7CiAgICAgcmV0dXJuKHJvdW5kKHJlYWRNYXQocGFzdGUwKHRyYWluX3B0X2Rpciwgc3ByaW50ZigiJTA0ZCIsIGluZGV4KSwgIi5tYXQiKSlbWzFdXSwwKSkKfQoKI2xvYWQgZmlkdWNpYWwgcG9pbnRzCmZpZHVjaWFsX3B0X2xpc3QgPC0gbGFwcGx5KDE6bl9maWxlcywgcmVhZE1hdC5tYXRyaXgpCnNhdmUoZmlkdWNpYWxfcHRfbGlzdCwgZmlsZT0iLi4vb3V0cHV0L2ZpZHVjaWFsX3B0X2xpc3QuUkRhdGEiKQpgYGAKCiMjIyBTdGVwIDM6IGNvbnN0cnVjdCBmZWF0dXJlcyBhbmQgcmVzcG9uc2VzCgorIFRoZSBmb2xsb3cgcGxvdHMgc2hvdyBob3cgcGFpcndpc2UgZGlzdGFuY2UgYmV0d2VlbiBmaWR1Y2lhbCBwb2ludHMgY2FuIHdvcmsgYXMgZmVhdHVyZSBmb3IgZmFjaWFsIGVtb3Rpb24gcmVjb2duaXRpb24uCgogICsgSW4gdGhlIGZpcnN0IGNvbHVtbiwgNzggZmlkdWNpYWxzIHBvaW50cyBvZiBlYWNoIGVtb3Rpb24gYXJlIG1hcmtlZCBpbiBvcmRlci4gCiAgKyBJbiB0aGUgc2Vjb25kIGNvbHVtbiBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlIGJldHdlZW4gcmlnaHQgcHVwaWwoMSkgYW5kICByaWdodCBicm93IHBlYWsoMjEpIGFyZSBzaG93biBpbiAgaGlzdG9ncmFtcy4gRm9yIGV4YW1wbGUsIHRoZSBkaXN0YW5jZSBvZiBhbiBhbmdyeSBmYWNlIHRlbmRzIHRvIGJlIHNob3J0ZXIgdGhhbiB0aGF0IG9mIGEgc3VycHJpc2VkIGZhY2UuCiAgKyBUaGUgdGhpcmQgY29sdW1uIGlzIHRoZSBkaXN0cmlidXRpb25zIG9mIHZlcnRpY2FsIGRpc3RhbmNlcyBiZXR3ZWVuIHJpZ2h0IG1vdXRoIGNvcm5lcig1MCkKYW5kIHRoZSBtaWRwb2ludCBvZiB0aGUgdXBwZXIgbGlwKDUyKS4gIEZvciBleGFtcGxlLCB0aGUgZGlzdGFuY2Ugb2YgYW4gaGFwcHkgZmFjZSB0ZW5kcyB0byBiZSBzaG9ydGVyIHRoYW4gdGhhdCBvZiBhIHNhZCBmYWNlLgoKIVtGaWd1cmUxXSguLi9maWdzL2ZlYXR1cmVfdmlzdWFsaXphdGlvbi5qcGcpCgpgZmVhdHVyZS5SYCBpcyB0aGUgd3JhcHBlciBmb3IgYWxsIGZlYXR1cmUgZW5naW5lZXJpbmcgZnVuY3Rpb25zIGFuZCBvcHRpb25zLiBUaGUgZnVuY3Rpb24gYGZlYXR1cmUoIClgIGhhdmUgb3B0aW9ucyB0aGF0IGNvcnJlc3BvbmQgdG8gZGlmZmVyZW50IHNjZW5hcmlvcyBmb3IgdGhlIHByb2plY3QgYW5kIHByb2R1Y2VzIGFuIFIgb2JqZWN0IHRoYXQgY29udGFpbnMgZmVhdHVyZXMgYW5kIHJlc3BvbnNlcyB0aGF0IGFyZSByZXF1aXJlZCBieSBhbGwgdGhlIG1vZGVscyB0aGF0IGFyZSBnb2luZyB0byBiZSBldmFsdWF0ZWQgbGF0ZXIuIAogIAogICsgYGZlYXR1cmUuUmAKICArIElucHV0OiBsaXN0IG9mIGltYWdlcyBvciBmaWR1Y2lhbCBwb2ludAogICsgT3V0cHV0OiBhbiBSRGF0YSBmaWxlIHRoYXQgY29udGFpbnMgZXh0cmFjdGVkIGZlYXR1cmVzIGFuZCBjb3JyZXNwb25kaW5nIHJlc3BvbnNlcwoKYGBge3IgZmVhdHVyZX0Kc291cmNlKCIuLi9saWIvZmVhdHVyZS5SIikKdG1fZmVhdHVyZV90cmFpbiA8LSBOQQppZihydW4uZmVhdHVyZS50cmFpbil7CiAgdG1fZmVhdHVyZV90cmFpbiA8LSBzeXN0ZW0udGltZShkYXRfdHJhaW48LWZlYXR1cmUoZmlkdWNpYWxfcHRfbGlzdCx0cmFpbl9pZHgsIHJ1bi5wb2x5LmZlYXR1cmUsIHJ1bi5hZGQucG9seS5mZWF0dXJlKSkKICBzYXZlKGRhdF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdHJhaW4uUkRhdGEiKQp9ZWxzZXsKICBsb2FkKGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKfQoKdG1fZmVhdHVyZV90ZXN0IDwtIE5BCmlmKHJ1bi5mZWF0dXJlLnRlc3QpewogIHRtX2ZlYXR1cmVfdGVzdCA8LSBzeXN0ZW0udGltZShkYXRfdGVzdCA8LSBmZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsIHRlc3RfaWR4LCBydW4ucG9seS5mZWF0dXJlLCBydW4uYWRkLnBvbHkuZmVhdHVyZSkpCiAgc2F2ZShkYXRfdGVzdCwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn1lbHNlewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn0KYGBgCgojIyMgU3RlcCA0OiBUcmFpbiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHdpdGggdHJhaW5pbmcgZmVhdHVyZXMgYW5kIHJlc3BvbnNlcwpDYWxsIHRoZSB0cmFpbiBtb2RlbCBhbmQgdGVzdCBtb2RlbCBmcm9tIGxpYnJhcnkuIAoKYHRyYWluLlJgIGFuZCBgdGVzdC5SYCBzaG91bGQgYmUgd3JhcHBlcnMgZm9yIGFsbCB5b3VyIG1vZGVsIHRyYWluaW5nIHN0ZXBzIGFuZCB5b3VyIGNsYXNzaWZpY2F0aW9uL3ByZWRpY3Rpb24gc3RlcHMuIAoKKyBgdHJhaW4uUmAKICArIElucHV0OiBhIGRhdGEgZnJhbWUgY29udGFpbmluZyBmZWF0dXJlcyBhbmQgbGFiZWxzIGFuZCBhIHBhcmFtZXRlciBsaXN0LgogICsgT3V0cHV0OmEgdHJhaW5lZCBtb2RlbAorIGB0ZXN0LlJgCiAgKyBJbnB1dDogdGhlIGZpdHRlZCBjbGFzc2lmaWNhdGlvbiBtb2RlbCB1c2luZyB0cmFpbmluZyBkYXRhIGFuZCBwcm9jZXNzZWQgZmVhdHVyZXMgZnJvbSB0ZXN0aW5nIGltYWdlcyAKICArIElucHV0OiBhbiBSIG9iamVjdCB0aGF0IGNvbnRhaW5zIGEgdHJhaW5lZCBjbGFzc2lmaWVyLgogICsgT3V0cHV0OiB0cmFpbmluZyBtb2RlbCBzcGVjaWZpY2F0aW9uCgorIEluIHRoaXMgU3RhcnRlciBDb2RlLCB3ZSB1c2UgbG9naXN0aWMgcmVncmVzc2lvbiB3aXRoIExBU1NPIHBlbmFsdHkgdG8gZG8gY2xhc3NpZmljYXRpb24uIAoKYGBge3IgbG9hZGxpYn0Kc291cmNlKCIuLi9saWIvdHJhaW4uUiIpIApzb3VyY2UoIi4uL2xpYi90ZXN0LlIiKQpgYGAKCiMjIyMgTW9kZWwgc2VsZWN0aW9uIHdpdGggY3Jvc3MtdmFsaWRhdGlvbgoqIERvIG1vZGVsIHNlbGVjdGlvbiBieSBjaG9vc2luZyBhbW9uZyBkaWZmZXJlbnQgdmFsdWVzIG9mIHRyYWluaW5nIG1vZGVsIHBhcmFtZXRlcnMuCgpgYGB7ciBydW5jdn0Kc291cmNlKCIuLi9saWIvY3Jvc3NfdmFsaWRhdGlvbi5SIikKZmVhdHVyZV90cmFpbiA9IGFzLm1hdHJpeChkYXRfdHJhaW5bLCAxOm5jb2woZGF0X3RyYWluKS0xXSkKbGFiZWxfdHJhaW4gPSBhcy5pbnRlZ2VyKGRhdF90cmFpbiRsYWJlbCkKaWYocnVuLmN2KXsKICByZXNfY3YgPC0gbWF0cml4KDAsIG5yb3cgPSBsZW5ndGgobG1iZCksIG5jb2wgPSA0KQogIGZvcihpIGluIDE6bGVuZ3RoKGxtYmQpKXsKICAgIGNhdCgibGFtYmRhID0gIiwgbG1iZFtpXSwgIlxuIikKICAgIHJlc19jdltpLF0gPC0gY3YuZnVuY3Rpb24oZmVhdHVyZXMgPSBmZWF0dXJlX3RyYWluLCBsYWJlbHMgPSBsYWJlbF90cmFpbiwgSywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGwgPSBsbWJkW2ldLCByZXdlaWdodCA9IHNhbXBsZS5yZXdlaWdodCkKICBzYXZlKHJlc19jdiwgZmlsZT0iLi4vb3V0cHV0L3Jlc19jdi5SRGF0YSIpCiAgfQp9ZWxzZXsKICBsb2FkKCIuLi9vdXRwdXQvcmVzX2N2LlJEYXRhIikKfQpgYGAKCiMgQWR2YW5jZWQgTW9kZWxzOgoKQ3JlYXRlIHdlaWdodCB0ZXN0CgpgYGB7cn0KbGFiZWxfdGVzdCA8LSBhcy5pbnRlZ2VyKGRhdF90ZXN0JGxhYmVsKQp3ZWlnaHRfdGVzdCA8LSByZXAoTkEsIGxlbmd0aChsYWJlbF90ZXN0KSkKZm9yIChpIGluIHVuaXF1ZShsYWJlbF90ZXN0KSl7CiAgd2VpZ2h0X3Rlc3RbbGFiZWxfdGVzdCA9PSBpXSA9IDAuNSAqIGxlbmd0aChsYWJlbF90ZXN0KSAvIGxlbmd0aChsYWJlbF90ZXN0W2xhYmVsX3Rlc3QgPT0gaV0pCn0KIyB0cmFpbmluZyB3ZWlnaHRzCndlaWdodF90cmFpbiA8LSByZXAoTkEsIGxlbmd0aChsYWJlbF90cmFpbikpCmZvciAodiBpbiB1bmlxdWUobGFiZWxfdHJhaW4pKXsKICB3ZWlnaHRfdHJhaW5bbGFiZWxfdHJhaW4gPT0gdl0gPSAwLjUgKiBsZW5ndGgobGFiZWxfdHJhaW4pIC8gbGVuZ3RoKGxhYmVsX3RyYWluW2xhYmVsX3RyYWluID09IHZdKQp9CgppZiAocnVuLmdibSl7CiAgaWYgKHNhbXBsZS5yZXdlaWdodCl7CiAgICB0bV90cmFpbiA8LSBzeXN0ZW0udGltZShmaXRfdHJhaW4gPC0gdHJhaW5fZ2JtKGRhdF90cmFpbiwgcz0wLjEsIEs9Sywgbj1nYm0ubnVtdHJlZXMsdyA9IHdlaWdodF90cmFpbikpCiAgfSBlbHNlIHsKICAgIHRtX3RyYWluIDwtIHN5c3RlbS50aW1lKGZpdF90cmFpbiA8LSB0cmFpbl9nYm0oZGF0X3RyYWluLCBzPTAuMSwgSz1LLCBuPWdibS5udW10cmVlcyx3ID0gTlVMTCkpCiAgfQogIAogICMgcGxvdCB0aGUgcGVyZm9ybWFuY2UKICBiZXN0Lml0ZXIub29iIDwtIGdibS5wZXJmKGZpdF90cmFpbixtZXRob2Q9Ik9PQiIpICAjIHJldHVybnMgb3V0LW9mLWJhZyBlc3RpbWF0ZWQgYmVzdCBudW1iZXIgb2YgdHJlZXMKICBwcmludChiZXN0Lml0ZXIub29iKQogIGJlc3QuaXRlci5jdiA8LSBnYm0ucGVyZihmaXRfdHJhaW4sbWV0aG9kPSJjdiIpICAgIyByZXR1cm5zIEstZm9sZCBjdiBlc3RpbWF0ZSBvZiBiZXN0IG51bWJlciBvZiB0cmVlcwogIHByaW50KGJlc3QuaXRlci5jdikKCn0gZWxzZSB7CiAgaWYgKHNhbXBsZS5yZXdlaWdodCl7CiAgICB0bV90cmFpbiA8LSBzeXN0ZW0udGltZShmaXRfdHJhaW4gPC0gdHJhaW4oZmVhdHVyZV90cmFpbiwgbGFiZWxfdHJhaW4sIHcgPSB3ZWlnaHRfdHJhaW4sIHBhcl9iZXN0KSkKICB9IGVsc2UgewogICAgdG1fdHJhaW4gPC0gc3lzdGVtLnRpbWUoZml0X3RyYWluIDwtIHRyYWluKGZlYXR1cmVfdHJhaW4sIGxhYmVsX3RyYWluLCB3ID0gTlVMTCwgcGFyX2Jlc3QpKQogIH0KfQpzYXZlKGZpdF90cmFpbiwgZmlsZT0iLi4vb3V0cHV0L2ZpdF90cmFpbi5SRGF0YSIpCmBgYAoKCgojIyMgU3RlcCA1OiBSdW4gdGVzdCBvbiB0ZXN0IGltYWdlcwpgYGB7ciB0ZXN0fQp0bV90ZXN0ID0gTkEKZmVhdHVyZV90ZXN0IDwtIGFzLm1hdHJpeChkYXRfdGVzdFssIDE6bmNvbChkYXRfdGVzdCktMV0pCmlmKHJ1bi50ZXN0KXsKICBsb2FkKGZpbGU9Ii4uL291dHB1dC9maXRfdHJhaW4uUkRhdGEiKQogIGlmIChydW4uZ2JtKXsKICAgIHRtX3Rlc3QgPC0gc3lzdGVtLnRpbWUocHJvYl9wcmVkPC10ZXN0X2dibShmaXRfdHJhaW4sYXMuZGF0YS5mcmFtZShmZWF0dXJlX3Rlc3QpLG49YmVzdC5pdGVyLmN2LHByZWQudHlwZSA9ICdyZXNwb25zZScpKQogICAgCiAgICBsYWJlbF9wcmVkIDwtIGNvbG5hbWVzKHByb2JfcHJlZClbYXBwbHkocHJvYl9wcmVkLCAxLCB3aGljaC5tYXgpXQogICAgCiAgfSBlbHNlIHsKICAgIHRtX3Rlc3QgPC0gc3lzdGVtLnRpbWUoe2xhYmVsX3ByZWQgPC0gYXMuaW50ZWdlcih0ZXN0KGZpdF90cmFpbiwgZmVhdHVyZV90ZXN0LCBwcmVkLnR5cGUgPSAnY2xhc3MnKSk7IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYl9wcmVkIDwtIHRlc3QoZml0X3RyYWluLCBmZWF0dXJlX3Rlc3QsIHByZWQudHlwZSA9ICdyZXNwb25zZScpfSkgIAogIH0KICAKfQpgYGAKClJhbmRvbSBGb3Jlc3Q6CgojIyBSZWJhbGFuY2UgVHJhaW4gU2V0CgpgYGB7cn0KaWYocnVuLnJmKXsKICAjIHRyYW5zZmVyIGxhYmVsIGNvbHVtbiBmcm9tIGZhY3RvciB0byBudW1lcmljCiAgZGF0X3RyYWluJGxhYmVsIDwtIGFzLm51bWVyaWMoZGF0X3RyYWluJGxhYmVsKS0xCiAgZGF0X3Rlc3QkbGFiZWwgPC0gYXMubnVtZXJpYyhkYXRfdGVzdCRsYWJlbCktMQogICNSZWJhbGFuY2luZyB0cmFpbmluZyBkYXRhLUJvb3RzdHJhcCBSYW5kb20gT3Zlci1TYW1wbGluZyBFeGFtcGxlcyBUZWNobmlxdWUgKFJPU0UpIHNvdXJjZQogIGlmKHJ1bi5iYWxhbmNlZC5kYXRhKXsKICBkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZTwtUk9TRShsYWJlbH4uLCBkYXRfdHJhaW4sc2VlZD0yMDIwKSRkYXRhCiAgc2F2ZShkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSwgZmlsZT0iLi4vb3V0cHV0L2JhbGFuY2VkX2RhdGEuUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9iYWxhbmNlZF9kYXRhLlJEYXRhIikKICB9CiAgdGFibGUoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UkbGFiZWwpCn0KYGBgCgoKIyMgVHVuZSBSRgpgYGB7cn0KaWYocnVuLnJmKXsKICBzb3VyY2UoIi4uL2xpYi9yYW5kb21fZm9yZXN0LlIiKQogIGlmKHR1bmUucmFuZG9tLmZvcmVzdCl7CiAgdGltZS5yZi50dW5lIDwtIHN5c3RlbS50aW1lKHJmLnR1bmUgPC0gcmFuZG9tX2ZvcmVzdF90dW5lKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlKSkKICBzYXZlKHJmLnR1bmUsIGZpbGU9Ii4uL291dHB1dC9yZl90dW5lLlJEYXRhIikKICB9ZWxzZSgKICAgIGxvYWQoIi4uL291dHB1dC9yZl90dW5lLlJEYXRhIikKICApCiAgcmYudHVuZQp9CgpgYGAKbXRyeSA9IDE1NCBpcyB0aGUgYmVzdC4KCiMjIEZpbmQgdGhlIGJlc3QgbnRyZWVzCmBgYHtyfQppZihydW4ucmYpewogIHNvdXJjZSgiLi4vbGliL3JhbmRvbV9mb3Jlc3QuUiIpCiAgI1RyYWluIDUwMAogIGlmKHR1bmUucmFuZG9tLmZvcmVzdCl7CiAgdGltZS5yZi50cmFpbiA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF81MDAgPC0gcmFuZG9tX2ZvcmVzdF90cmFpbl81MDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDE1NCkpCiAgc2F2ZShyYW5kb21fZm9yZXN0X2ZpdF81MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzUwMF90cmVlcy5SRGF0YSIpCiAgfQogICNUZXN0IDUwMAogIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwPU5BCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JmX3RyYWluXzUwMF90cmVlcy5SRGF0YSIpCiAgICB0aW1lLnJmLnRlc3QgPC0gc3lzdGVtLnRpbWUoCiAgICAgIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwIDwtIHJhbmRvbV9mb3Jlc3RfdGVzdCgKICAgICAgbW9kZWwgPSByYW5kb21fZm9yZXN0X2ZpdF81MDAsdGVzdHNldCA9IGRhdF90ZXN0KQogICAgICApCiAgICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCkpCiAgICBhY2N1X3JmX3Rlc3QgPC0gbWVhbihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA9PSBkYXRfdGVzdCRsYWJlbCkKICAgIHJhbmRvbV9mb3Jlc3RfbGFiZWw8LXJvdW5kKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwKQogICAgYWNjdV9yZiA8LSBzdW0od2VpZ2h0X3Rlc3QgKiAocmFuZG9tX2ZvcmVzdF9sYWJlbCA9PSBsYWJlbF90ZXN0KSkgLyBzdW0od2VpZ2h0X3Rlc3QpCiAgICAjcHJvYl9wcmVkIDwtIGxhYmxlX3ByZWQKICAgIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAsIGxhYmVsX3Rlc3QsIHdlaWdodF90ZXN0KQogICAgYXVjX3JmIDwtIFdlaWdodGVkQVVDKHRwci5mcHIpCiAgICBjYXQoIlRoZSBBVUMgb2YgbW9kZWwgYWZ0ZXIgcmV3ZWlnaHRpbmc6IFJGIiwgImlzIiwgYXVjX3JmLCAiLlxuIikKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGltYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZl90ZXN0KjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZioxMDAsICIlLlxuIikKICAgIGNhdCgiVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsIHRpbWUucmYudHJhaW5bMV0sICJzIFxuIikKICAgIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIix0aW1lLnJmLnRlc3RbMV0sICJzIFxuIikKICAgIH0KICAjIFRoZSBBVUMgb2YgbW9kZWwgYWZ0ZXIgcmV3ZWlnaHRpbmc6IFJGIGlzIDAuNTAzMTk5OSAuCiAgIyBUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gaW1iYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgODAuMzMzMzMgJS4KICAjIFRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBiYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgNTAuMzE5OTkgJS4KICAjIFRpbWUgZm9yIHRyYWluaW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAgMjAuOTUgcyAKICAjIFRpbWUgZm9yIHRlc3RpbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICAwLjA5IHMgCgogICNUcmFpbiAxMDAwCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgIHRpbWUucmYudHJhaW4gPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMTAwMCA8LSByYW5kb21fZm9yZXN0X3RyYWluXzEwMDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDE1NCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzEwMDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzEwMDBfdHJlZXMuUkRhdGEiKQogICAgfQogICNUZXN0IDEwMDAKICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcD1OQQogIGlmKHR1bmUucmFuZG9tLmZvcmVzdCl7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yZl90cmFpbl8xMDAwX3RyZWVzLlJEYXRhIikKICAgIHRpbWUucmYudGVzdCA8LSBzeXN0ZW0udGltZSgKICAgIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwIDwtIHJhbmRvbV9mb3Jlc3RfdGVzdCgKICAgICAgbW9kZWwgPSByYW5kb21fZm9yZXN0X2ZpdF8xMDAwLHRlc3RzZXQgPSBkYXRfdGVzdCkKICAgICkKICAgIAogICAgcmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXApKQogICAgYWNjdV9yZl90ZXN0IDwtIG1lYW4ocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAgPT0gZGF0X3Rlc3QkbGFiZWwpCiAgICByYW5kb21fZm9yZXN0X2xhYmVsPC1yb3VuZChyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCkKICAgIGFjY3VfcmYgPC0gc3VtKHdlaWdodF90ZXN0ICogKHJhbmRvbV9mb3Jlc3RfbGFiZWwgPT0gbGFiZWxfdGVzdCkpIC8gc3VtKHdlaWdodF90ZXN0KQogICAgI3Byb2JfcHJlZCA8LSBsYWJsZV9wcmVkCiAgICB0cHIuZnByIDwtIFdlaWdodGVkUk9DKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwLCBsYWJlbF90ZXN0LCB3ZWlnaHRfdGVzdCkKICAgIGF1Y19yZiA8LSBXZWlnaHRlZEFVQyh0cHIuZnByKQogICAgY2F0KCJUaGUgQVVDIG9mIG1vZGVsIGFmdGVyIHJld2VpZ2h0aW5nOiBSRiIsICJpcyIsIGF1Y19yZiwgIi5cbiIpCiAgICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBpbWJhbGFuY2VkIHRlc3RpbmcgZGF0YSIsICJpcyIsIGFjY3VfcmZfdGVzdCoxMDAsICIlLlxuIikKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGJhbGFuY2VkIHRlc3RpbmcgZGF0YSIsICJpcyIsIGFjY3VfcmYqMTAwLCAiJS5cbiIpCiAgICBjYXQoIlRpbWUgZm9yIHRyYWluaW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAiLCB0aW1lLnJmLnRyYWluWzFdLCAicyBcbiIpCiAgICBjYXQoIlRpbWUgZm9yIHRlc3RpbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsdGltZS5yZi50ZXN0WzFdLCAicyBcbiIpCiAgICB9CgogICNUcmFpbiAxNTAwCiAgICBpZih0dW5lLnJhbmRvbS5mb3Jlc3QpewogICAgICB0aW1lLnJmLnRyYWluIDwtIHN5c3RlbS50aW1lKHJhbmRvbV9mb3Jlc3RfZml0XzE1MDAgPC0gcmFuZG9tX2ZvcmVzdF90cmFpbl8xNTAwKGRhdF90cmFpbl9iYWxhbmNlZF9yb3NlLG10cnkgPSAxNTQpKQogICAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzE1MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzE1MDBfdHJlZXMuUkRhdGEiKQogICAgICB9CiAgI1Rlc3QgMTUwMAogIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwPU5BCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JmX3RyYWluXzE1MDBfdHJlZXMuUkRhdGEiKQogICAgdGltZS5yZi50ZXN0IDwtIHN5c3RlbS50aW1lKAogICAgICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA8LSByYW5kb21fZm9yZXN0X3Rlc3QoCiAgICAgICAgbW9kZWwgPSByYW5kb21fZm9yZXN0X2ZpdF8xNTAwLHRlc3RzZXQgPSBkYXRfdGVzdCkKICAgICAgKQogICAgCiAgICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCkpCiAgICBhY2N1X3JmX3Rlc3QgPC0gbWVhbihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA9PSBkYXRfdGVzdCRsYWJlbCkKICAgIHJhbmRvbV9mb3Jlc3RfbGFiZWw8LXJvdW5kKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwKQogICAgYWNjdV9yZiA8LSBzdW0od2VpZ2h0X3Rlc3QgKiAocmFuZG9tX2ZvcmVzdF9sYWJlbCA9PSBsYWJlbF90ZXN0KSkgLyBzdW0od2VpZ2h0X3Rlc3QpCiAgICAjcHJvYl9wcmVkIDwtIGxhYmxlX3ByZWQKICAgIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAsIGxhYmVsX3Rlc3QsIHdlaWdodF90ZXN0KQogICAgYXVjX3JmIDwtIFdlaWdodGVkQVVDKHRwci5mcHIpCiAgICBjYXQoIlRoZSBBVUMgb2YgbW9kZWwgYWZ0ZXIgcmV3ZWlnaHRpbmc6IFJGIiwgImlzIiwgYXVjX3JmLCAiLlxuIikKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGltYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZl90ZXN0KjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZioxMDAsICIlLlxuIikKICAgIGNhdCgiVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsIHRpbWUucmYudHJhaW5bMV0sICJzIFxuIikKICAgIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIix0aW1lLnJmLnRlc3RbMV0sICJzIFxuIikKICAgIH0KCiAgI1RyYWluIDIwMDAKICBpZih0dW5lLnJhbmRvbS5mb3Jlc3QpewogICAgdGltZS5yZi50cmFpbiA8LSBzeXN0ZW0udGltZShyYW5kb21fZm9yZXN0X2ZpdF8yMDAwIDwtIHJhbmRvbV9mb3Jlc3RfdHJhaW5fMjAwMChkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMTU0KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXRfMjAwMCwgZmlsZSA9ICIuLi9vdXRwdXQvcmZfdHJhaW5fMjAwMF90cmVlcy5SRGF0YSIpCiAgICB9CiAgI1Rlc3QgMjAwMAogIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwPU5BCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JmX3RyYWluXzIwMDBfdHJlZXMuUkRhdGEiKQogICAgdGltZS5yZi50ZXN0IDwtIHN5c3RlbS50aW1lKAogICAgICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA8LSByYW5kb21fZm9yZXN0X3Rlc3QoCiAgICAgICAgbW9kZWwgPSByYW5kb21fZm9yZXN0X2ZpdF8yMDAwLHRlc3RzZXQgPSBkYXRfdGVzdCkKICAgICAgKQoKICAgIHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwKSkKICAgIGFjY3VfcmZfdGVzdCA8LSBtZWFuKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwID09IGRhdF90ZXN0JGxhYmVsKQogICAgcmFuZG9tX2ZvcmVzdF9sYWJlbDwtcm91bmQocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXApCiAgICBhY2N1X3JmIDwtIHN1bSh3ZWlnaHRfdGVzdCAqIChyYW5kb21fZm9yZXN0X2xhYmVsID09IGxhYmVsX3Rlc3QpKSAvIHN1bSh3ZWlnaHRfdGVzdCkKICAgICNwcm9iX3ByZWQgPC0gbGFibGVfcHJlZAogICAgdHByLmZwciA8LSBXZWlnaHRlZFJPQyhyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCwgbGFiZWxfdGVzdCwgd2VpZ2h0X3Rlc3QpCiAgICBhdWNfcmYgPC0gV2VpZ2h0ZWRBVUModHByLmZwcikKICAgIGNhdCgiVGhlIEFVQyBvZiBtb2RlbCBhZnRlciByZXdlaWdodGluZzogUkYiLCAiaXMiLCBhdWNfcmYsICIuXG4iKQogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gaW1iYWxhbmNlZCB0ZXN0aW5nIGRhdGEiLCAiaXMiLCBhY2N1X3JmX3Rlc3QqMTAwLCAiJS5cbiIpCiAgICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBiYWxhbmNlZCB0ZXN0aW5nIGRhdGEiLCAiaXMiLCBhY2N1X3JmKjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaW1lIGZvciB0cmFpbmluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIiwgdGltZS5yZi50cmFpblsxXSwgInMgXG4iKQogICAgY2F0KCJUaW1lIGZvciB0ZXN0aW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAiLHRpbWUucmYudGVzdFsxXSwgInMgXG4iKQogICAgfQogICNUcmFpbiAyNTAwCiAgaWYodHVuZS5yYW5kb20uZm9yZXN0KXsKICAgIHRpbWUucmYudHJhaW4gPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXRfMjUwMCA8LSByYW5kb21fZm9yZXN0X3RyYWluXzI1MDAoZGF0X3RyYWluX2JhbGFuY2VkX3Jvc2UsbXRyeSA9IDE1NCkpCiAgICBzYXZlKHJhbmRvbV9mb3Jlc3RfZml0XzI1MDAsIGZpbGUgPSAiLi4vb3V0cHV0L3JmX3RyYWluXzI1MDBfdHJlZXMuUkRhdGEiKQogICAgfQogICNUZXN0IDI1MDAKICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcD1OQQogIGlmKHR1bmUucmFuZG9tLmZvcmVzdCl7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yZl90cmFpbl8yNTAwX3RyZWVzLlJEYXRhIikKICAgIHRpbWUucmYudGVzdCA8LSBzeXN0ZW0udGltZSgKICAgICAgcmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAgPC0gcmFuZG9tX2ZvcmVzdF90ZXN0KAogICAgICAgIG1vZGVsID0gcmFuZG9tX2ZvcmVzdF9maXRfMjUwMCx0ZXN0c2V0ID0gZGF0X3Rlc3QpCiAgICAgICkKCiAgICByYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCkpCiAgICBhY2N1X3JmX3Rlc3QgPC0gbWVhbihyYW5kb21fZm9yZXN0X3Rlc3RfcHJlcCA9PSBkYXRfdGVzdCRsYWJlbCkKICAgIHJhbmRvbV9mb3Jlc3RfbGFiZWw8LXJvdW5kKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwKQogICAgYWNjdV9yZiA8LSBzdW0od2VpZ2h0X3Rlc3QgKiAocmFuZG9tX2ZvcmVzdF9sYWJlbCA9PSBsYWJlbF90ZXN0KSkgLyBzdW0od2VpZ2h0X3Rlc3QpCiAgICAjcHJvYl9wcmVkIDwtIGxhYmxlX3ByZWQKICAgIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAsIGxhYmVsX3Rlc3QsIHdlaWdodF90ZXN0KQogICAgYXVjX3JmIDwtIFdlaWdodGVkQVVDKHRwci5mcHIpCiAgICBjYXQoIlRoZSBBVUMgb2YgbW9kZWwgYWZ0ZXIgcmV3ZWlnaHRpbmc6IFJGIiwgImlzIiwgYXVjX3JmLCAiLlxuIikKICAgIGNhdCgiVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGltYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZl90ZXN0KjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gYmFsYW5jZWQgdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZioxMDAsICIlLlxuIikKICAgIGNhdCgiVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsIHRpbWUucmYudHJhaW5bMV0sICJzIFxuIikKICAgIGNhdCgiVGltZSBmb3IgdGVzdGluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIix0aW1lLnJmLnRlc3RbMV0sICJzIFxuIikKICAgIH0KfQoKYGBgClRlc3RpbmcgUmVzdWx0OgpXaGVuIHRyZWVzID0gNTAwOgpUaGUgQVVDIG9mIG1vZGVsIGFmdGVyIHJld2VpZ2h0aW5nOiBSRiBpcyAwLjUxMTY3NDUgLgpUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gaW1iYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgODAuNjY2NjcgJS4KVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGJhbGFuY2VkIHRlc3RpbmcgZGF0YSBpcyA1MS4xNjc0NSAlLgpUaW1lIGZvciB0cmFpbmluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIDcxMy42MyBzIApUaW1lIGZvciB0ZXN0aW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAgMC4xOSBzIAoKV2hlbiB0cmVlcyA9IDEwMDAKVGhlIEFVQyBvZiBtb2RlbCBhZnRlciByZXdlaWdodGluZzogUkYgaXMgMC41MjAxNDkxIC4KVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGltYmFsYW5jZWQgdGVzdGluZyBkYXRhIGlzIDgxICUuClRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBiYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgNTIuMDE0OTEgJS4KVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICAxMzY3Ljk0IHMgClRpbWUgZm9yIHRlc3RpbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICAwLjI4IHMgCgpXaGVuIHRyZWVzID0gMTUwMApUaGUgQVVDIG9mIG1vZGVsIGFmdGVyIHJld2VpZ2h0aW5nOiBSRiBpcyAwLjUyMDE0OTEgLgpUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gaW1iYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgODEgJS4KVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGJhbGFuY2VkIHRlc3RpbmcgZGF0YSBpcyA1Mi4wMTQ5MSAlLgpUaW1lIGZvciB0cmFpbmluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIDIwNzcuNTYgcyAKVGltZSBmb3IgdGVzdGluZyBtb2RlbCBSYW5kb20gRm9yZXN0ID0gIDAuMzYgcyAKCldoZW4gdHJlZXMgPSAyMDAwClRoZSBBVUMgb2YgbW9kZWwgYWZ0ZXIgcmV3ZWlnaHRpbmc6IFJGIGlzIDAuNTIwMTQ5MSAuClRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBpbWJhbGFuY2VkIHRlc3RpbmcgZGF0YSBpcyA4MSAlLgpUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gYmFsYW5jZWQgdGVzdGluZyBkYXRhIGlzIDUyLjAxNDkxICUuClRpbWUgZm9yIHRyYWluaW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAgMzE0Mi43NyBzIApUaW1lIGZvciB0ZXN0aW5nIG1vZGVsIFJhbmRvbSBGb3Jlc3QgPSAgMC41NiBzIAoKV2hlbiB0cmVlcyA9IDI1MDAKVGhlIEFVQyBvZiBtb2RlbCBhZnRlciByZXdlaWdodGluZzogUkYgaXMgMC41MTU5MTE4IC4KVGhlIGFjY3VyYWN5IG9mIG1vZGVsOiBSYW5kb20gRm9yZXN0IG9uIGltYmFsYW5jZWQgdGVzdGluZyBkYXRhIGlzIDgwLjgzMzMzICUuClRoZSBhY2N1cmFjeSBvZiBtb2RlbDogUmFuZG9tIEZvcmVzdCBvbiBiYWxhbmNlZCB0ZXN0aW5nIGRhdGEgaXMgNTEuNTkxMTggJS4KVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICAzOTYzLjY3IHMgClRpbWUgZm9yIHRlc3RpbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICAwLjYyIHMgCgpUaGVyZWZvcmUsIHdlIHNob3VsZCB1c2UgdHJlZXMgPSAxMDAwLgoKIyMgVHJhaW4gUkYgd2l0aCB0dW5pbmcgcGFyYW1ldGVycwoKYGBge3J9CmlmKHJ1bi5yZil7CiAgc291cmNlKCIuLi9saWIvcmFuZG9tX2ZvcmVzdC5SIikKICBpZih0cmFpbi5yYW5kb20uZm9yZXN0KXsKICAgIHRpbWUucmYudHJhaW4gPC0gc3lzdGVtLnRpbWUocmFuZG9tX2ZvcmVzdF9maXQgPC0gcmFuZG9tX2ZvcmVzdF90cmFpbihkYXRfdHJhaW5fYmFsYW5jZWRfcm9zZSxtdHJ5ID0gMTU0KSkKICAgIHNhdmUocmFuZG9tX2ZvcmVzdF9maXQsIGZpbGUgPSAiLi4vb3V0cHV0L3JhbmRvbV9mb3Jlc3RfdHJhaW4uUkRhdGEiKQogICAgc2F2ZSh0aW1lLnJmLnRyYWluLGZpbGUgPSAiLi4vb3V0cHV0L3JhbmRvbV9mb3Jlc3RfdHJhaW5fdGltZS5SRGF0YSIpCiAgfWVsc2V7CiAgICBsb2FkKGZpbGUgPSAiLi4vb3V0cHV0L3JhbmRvbV9mb3Jlc3RfdHJhaW5fdGltZS5SRGF0YSIpCiAgICBsb2FkKGZpbGUgPSAiLi4vb3V0cHV0L3JhbmRvbV9mb3Jlc3RfdHJhaW4uUkRhdGEiKQogIH0KfQpgYGAKCiMjIFRlc3QgUkYgd2l0aCB0dW5pbmcgcGFyYW1ldGVycwoKYGBge3J9CmlmKHJ1bi5yZil7CiAgcmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXA9TkEKICBpZihydW4udGVzdCl7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9yYW5kb21fZm9yZXN0X3RyYWluLlJEYXRhIikKICAgIHRpbWUucmYudGVzdCA8LSBzeXN0ZW0udGltZSgKICAgICAgcmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAgPC0gcmFuZG9tX2ZvcmVzdF90ZXN0KAogICAgICAgIG1vZGVsID0gcmFuZG9tX2ZvcmVzdF9maXQsdGVzdHNldCA9IGRhdF90ZXN0KQogICAgKQogIH0KICAjIyByZXdlaWdodCB0aGUgdGVzdCBkYXRhIHRvIHJlcHJlc2VudCBhIGJhbGFuY2VkIGxhYmVsIGRpc3RyaWJ1dGlvbgogIGlmIChydW4uZ2JtKXsKICAgIGFjY3UgPC0gbWVhbihkYXRfdGVzdCRsYWJlbCA9PSBsYWJlbF9wcmVkKQogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgR0JNIGJhc2VsaW5lIG1vZGVsIGlzIiwgbWVhbihkYXRfdGVzdCRsYWJlbCA9PSBsYWJlbF9wcmVkKSoxMDAsICIlLlxuIikKICB9IGVsc2UgewogICAgbGFiZWxfdGVzdCA8LSBhcy5pbnRlZ2VyKGRhdF90ZXN0JGxhYmVsKQogICAgd2VpZ2h0X3Rlc3QgPC0gcmVwKE5BLCBsZW5ndGgobGFiZWxfdGVzdCkpCiAgICBmb3IgKHYgaW4gdW5pcXVlKGxhYmVsX3Rlc3QpKXsKICAgICAgd2VpZ2h0X3Rlc3RbbGFiZWxfdGVzdCA9PSB2XSA9IDAuNSAqIGxlbmd0aChsYWJlbF90ZXN0KSAvIGxlbmd0aChsYWJlbF90ZXN0W2xhYmVsX3Rlc3QgPT0gdl0pCiAgICB9CiAgCiAgICBhY2N1IDwtIHN1bSh3ZWlnaHRfdGVzdCAqIChsYWJlbF9wcmVkID09IGxhYmVsX3Rlc3QpKSAvIHN1bSh3ZWlnaHRfdGVzdCkKICAgIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MocHJvYl9wcmVkLCBsYWJlbF90ZXN0LCB3ZWlnaHRfdGVzdCkKICAgIGF1YyA8LSBXZWlnaHRlZEFVQyh0cHIuZnByKQogIAogICAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IiwgbW9kZWxfbGFiZWxzW3doaWNoLm1pbihyZXNfY3YkbWVhbl9lcnJvcildLCAiaXMiLCBhY2N1KjEwMCwgIiUuXG4iKQogICAgY2F0KCJUaGUgQVVDIG9mIG1vZGVsOiIsIG1vZGVsX2xhYmVsc1t3aGljaC5taW4ocmVzX2N2JG1lYW5fZXJyb3IpXSwgImlzIiwgYXVjLCAiLlxuIikKICB9CiAgcmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXApKQogIGFjY3VfcmZfdGVzdCA8LSBtZWFuKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwID09IGRhdF90ZXN0JGxhYmVsKQp9CmBgYAoKIyMgQ2FsY3VsYXRlIHdlaWdodGVkQVVDIG9uIHRlc3Rpbmcgc3BsaXQKCmBgYHtyfQppZihydW4ucmYpewogIHJhbmRvbV9mb3Jlc3RfbGFiZWw8LXJvdW5kKHJhbmRvbV9mb3Jlc3RfdGVzdF9wcmVwKQogICNwcm9iX3ByZWQgPC0gbGFibGVfcHJlZAogIHRwci5mcHIgPC0gV2VpZ2h0ZWRST0MocmFuZG9tX2ZvcmVzdF90ZXN0X3ByZXAsIGxhYmVsX3Rlc3QsIHdlaWdodF90ZXN0KQogIGF1Y19yZiA8LSBXZWlnaHRlZEFVQyh0cHIuZnByKQp9CmBgYAoKIyMgU3VtbWFyeSBvZiBSRgoKYGBge3J9CmlmKHJ1bi5yZil7CiAgY2F0KCJUaGUgQVVDIG9mIG1vZGVsIGFmdGVyIHJld2VpZ2h0aW5nOiBSRiIsICJpcyIsIGF1Y19yZiwgIi5cbiIpCiAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IFJhbmRvbSBGb3Jlc3Qgb24gdGVzdGluZyBkYXRhIiwgImlzIiwgYWNjdV9yZl90ZXN0KjEwMCwgIiUuXG4iKQogIGNhdCgiVGltZSBmb3IgdHJhaW5pbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsIHRpbWUucmYudHJhaW5bMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRlc3RpbmcgbW9kZWwgUmFuZG9tIEZvcmVzdCA9ICIsdGltZS5yZi50ZXN0WzFdLCAicyBcbiIpCiAgI2xhYmVsX3Rlc3QKCiAgY2F0KCJUaGUgYWNjdXJhY3kgb2YgbW9kZWw6IiwgbW9kZWxfbGFiZWxzW3doaWNoLm1pbihyZXNfY3YkbWVhbl9lcnJvcildLCAiaXMiLCBhY2N1KjEwMCwgIiUuXG4iKQogIGNhdCgiVGhlIEFVQyBvZiBtb2RlbDoiLCBtb2RlbF9sYWJlbHNbd2hpY2gubWluKHJlc19jdiRtZWFuX2Vycm9yKV0sICJpcyIsIGF1YywgIi5cbiIpCn0KYGBgCgojIyMgU3VtbWFyaXplIFJ1bm5pbmcgVGltZQoKUHJlZGljdGlvbiBwZXJmb3JtYW5jZSBtYXR0ZXJzLCBzbyBkb2VzIHRoZSBydW5uaW5nIHRpbWVzIGZvciBjb25zdHJ1Y3RpbmcgZmVhdHVyZXMgYW5kIGZvciB0cmFpbmluZyB0aGUgbW9kZWwsIGVzcGVjaWFsbHkgd2hlbiB0aGUgY29tcHV0YXRpb24gcmVzb3VyY2UgaXMgbGltaXRlZC4gCgpgYGB7ciBydW5uaW5nX3RpbWV9CiMgY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdHJhaW5pbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90cmFpblsxXSwgInMgXG4iKQojIGNhdCgiVGltZSBmb3IgY29uc3RydWN0aW5nIHRlc3RpbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90ZXN0WzFdLCAicyBcbiIpCiMgY2F0KCJUaW1lIGZvciB0cmFpbmluZyBtb2RlbD0iLCB0bV90cmFpblsxXSwgInMgXG4iKSAKIyBjYXQoIlRpbWUgZm9yIHRlc3RpbmcgbW9kZWw9IiwgdG1fdGVzdFsxXSwgInMgXG4iKQpgYGAKCgojIyMgU1ZNIE1vZGVsCgoqIEJhbGFuY2UgdGhlIFRyYWluaW5nIERhdGEKCmBgYHtyfQppZihydW4uc3ZtKXsKICB0bV9zdm1fcmViYWxhbmNlZF90cmFpbiA8LSBOQQogIGlmKG5lZWRzLmJhbGFuY2VkKXsKICAgIHRtX3N2bV9yZWJhbGFuY2VkX3RyYWluIDwtIHN5c3RlbS50aW1lKHN2bV90cmFpbmluZ19kYXRhIDwtIFJPU0UobGFiZWwgfiAuLCBkYXRhID0gZGF0X3RyYWluKSRkYXRhKQogICAgc2F2ZShzdm1fdHJhaW5pbmdfZGF0YSwgZmlsZT0iLi4vb3V0cHV0L3N2bV90cmFpbmluZ19kYXRhLlJEYXRhIikKICAgIHNhdmUodG1fc3ZtX3JlYmFsYW5jZWRfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmViYWxhbmNlZF90cmFpbi5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIHN2bV90cmFpbmluZ19kYXRhIDwtIGRhdF90cmFpbgogICAgdG1fc3ZtX3JlYmFsYW5jZWRfdHJhaW4gPC0gdG1fZmVhdHVyZV90cmFpbgogIH0KfSBlbHNlIHsKICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmViYWxhbmNlZF90cmFpbi5SRGF0YSIpCn0KCmBgYAoKKiBNb2RlbCBTZWxlY3Rpb24KCmBgYHtyfQppZihydW4uc3ZtKXsKICB0bV9zdm1fbGluZWFyX21vZCA8LSBOQQogIHRtX3N2bV9yYWRpYWxfbW9kIDwtIE5BCiAgCiAgaWYobW9kZWwuc2VsZWN0aW9uKXsKICAgIHN2bV9tb2RlbF9hdWMgPC0gcmVwKE5BLCAyKQogICAgCiAgICAjIyMgbGluZWFyIGtlcm5lbAogICAgaWYocnVuLmN2KXsKICAgICAgI2Jlc3QubGluZWFyLmNvc3QgPC0gc3ZtX2xpbmVhcl9jb3N0X3R1bmUoc3ZtX3RyYWluaW5nX2RhdGEpCiAgICAgICNjYXQoIlRoZSBiZXN0IGNvc3QgZm9yIHN2bSBtb2RlbCB3aXRoIGxpbmVhciBrZXJuZWwgaXM6ICIsIGJlc3QubGluZWFyLmNvc3QkYmVzdC5wYXJhbWV0ZXJzJGNvc3QpCiAgICAgIHRtX3N2bV9saW5lYXJfbW9kIDwtIHN5c3RlbS50aW1lKHN2bV9saW5lYXJfbW9kIDwtIHN2bV9saW5lYXJfdHJhaW4oc3ZtX3RyYWluaW5nX2RhdGEsIDAuMDEsIEspKQogICAgICBzYXZlKHN2bV9saW5lYXJfbW9kLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgICBzYXZlKHRtX3N2bV9saW5lYXJfbW9kLCBmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgfSBlbHNlIHsKICAgICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvc3ZtX2xpbmVhcl9tb2QuUkRhdGEiKQogICAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fbGluZWFyX21vZC5SRGF0YSIpCiAgICB9CiAgICBzdm1fbGluZWFyX3ByZWQgPC0gc3ZtX3Rlc3Qoc3ZtX2xpbmVhcl9tb2QsIHN2bV90cmFpbmluZ19kYXRhLCBUUlVFKQogICAgI21lYW4ocm91bmQoc3ZtX2xpbmVhcl9wcmVkID09IHN2bV90cmFpbmluZ19kYXRhJGxhYmVsKSkKICAgIHN2bV9saW5lYXJfYWNjdSA8LSBtZWFuKHJvdW5kKHN2bV9saW5lYXJfcHJlZCA9PSBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByX2xpbmVhciA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9saW5lYXJfcHJlZCksIHN2bV90cmFpbmluZ19kYXRhJGxhYmVsKQogICAgc3ZtX21vZGVsX2F1Y1sxXSA8LSBXZWlnaHRlZEFVQyh0cHIuZnByX2xpbmVhcikKICAgIAogICAgCiAgICAjIyMgcmFkaWFsIGJhc2lzIGtlcm5lbAogICAgaWYocnVuLmN2KXsKICAgICAgI2Jlc3QucmFkaWFsLmNvc3QgPC0gc3ZtX3JhZGlhbF9jb3N0X3R1bmUoc3ZtX3RyYWluaW5nX2RhdGEpCiAgICAgICNyYWRpYWxfY29zdCA9IGJlc3QucmFkaWFsLmNvc3QkYmVzdC5wYXJhbWV0ZXJzJGNvc3QKICAgICAgI3JhZGlhbF9nYW1tYSA9IGJlc3QucmFkaWFsLmNvc3QkYmVzdC5wYXJhbWV0ZXJzJGdhbW1hCiAgICAgIAogICAgICB0bV9zdm1fcmFkaWFsX21vZCA8IHN5c3RlbS50aW1lKHN2bV9yYWRpYWxfbW9kIDwtIHN2bV9yYWRpYWxfdHJhaW4oc3ZtX3RyYWluaW5nX2RhdGEsIDEsIEspKQogICAgICBzYXZlKHN2bV9yYWRpYWxfbW9kLCBmaWxlPSIuLi9vdXRwdXQvc3ZtX3JhZGlhbF9tb2QuUkRhdGEiKQogICAgICBzYXZlKHRtX3N2bV9yYWRpYWxfbW9kLCBmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JhZGlhbF9tb2QuUkRhdGEiKQogICAgfSBlbHNlIHsgCiAgICAgIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICAgICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JhZGlhbF9tb2QuUkRhdGEiKQogICAgfQogICAgc3ZtX3JhZGlhbF9wcmVkIDwtIHN2bV90ZXN0KHN2bV9yYWRpYWxfbW9kLCBzdm1fdHJhaW5pbmdfZGF0YSwgVFJVRSkKICAgICMgZXZhbHVhdGUgcGVyZm9ybWFuY2UKICAgIHN2bV9yYWRpYWxfYWNjdSA8LSBtZWFuKHJvdW5kKHN2bV9yYWRpYWxfcHJlZCA9PSBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByX2RlZmF1bHQgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhzdm1fcmFkaWFsX3ByZWQpLCBzdm1fdHJhaW5pbmdfZGF0YSRsYWJlbCkKICAgIHN2bV9tb2RlbF9hdWNbMl0gPC0gV2VpZ2h0ZWRBVUModHByLmZwcl9kZWZhdWx0KQogIH0gZWxzZSB7CiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fbGluZWFyX21vZC5SRGF0YSIpCiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fbGluZWFyX21vZC5SRGF0YSIpCiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCiAgfQogIAogIAogICMjIyBFdmFsdWF0aW9uIG9uIFRlc3RpbmcgRGF0YQogIHRtX3N2bV9yZWJhbGFuY2VkX3Rlc3QgPC0gTkEKICBpZihuZWVkcy5iYWxhbmNlZCl7CiAgICB0bV9zdm1fcmViYWxhbmNlZF90ZXN0IDwtIHN5c3RlbS50aW1lKHN2bV90ZXN0aW5nX2RhdGEgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdGVzdCkkZGF0YSkKICAgIHNhdmUoc3ZtX3Rlc3RpbmdfZGF0YSwgZmlsZT0iLi4vb3V0cHV0L3N2bV90ZXN0aW5nX2RhdGEuUkRhdGEiKQogICAgc2F2ZSh0bV9zdm1fcmViYWxhbmNlZF90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JlYmFsYW5jZWRfdGVzdC5SRGF0YSIpCiAgfSBlbHNlIHsKICAgIHN2bV90ZXN0aW5nX2RhdGEgPC0gZGF0X3Rlc3QKICAgIHRtX3N2bV9yZWJhbGFuY2VkX3Rlc3QgPC0gdG1fZmVhdHVyZV90ZXN0CiAgfQogIAogIGlmKHJ1bi5zdm0udGVzdCl7CiAgICBzdm1fYXVjIDwtIHJlcChOQSwgMikKICAgIHN2bV9hY2N1IDwtIHJlcChOQSwgMikKICAgICMjIGxpbmVhcgogICAgdG1fc3ZtX2xpbmVhcl90ZXN0IDwtIHN5c3RlbS50aW1lKHN2bV9saW5lYXJfcHJlZCA8LSBzdm1fdGVzdChzdm1fbGluZWFyX21vZCwgc3ZtX3Rlc3RpbmdfZGF0YSkpCiAgICBzdm1fYWNjdVsxXSA9IG1lYW4ocm91bmQoc3ZtX2xpbmVhcl9wcmVkID09IHN2bV90ZXN0aW5nX2RhdGEkbGFiZWwpKQogICAgdHByLmZwci5saW5lYXIgPC0gV2VpZ2h0ZWRST0MoYXMubnVtZXJpYyhzdm1fbGluZWFyX3ByZWQpLCBzdm1fdGVzdGluZ19kYXRhJGxhYmVsKQogICAgc3ZtX2F1Y1sxXSA9IFdlaWdodGVkQVVDKHRwci5mcHIubGluZWFyKQogICAgIyMgcmJmCiAgICB0bV9zdm1fcmJmX3Rlc3QgPC0gc3lzdGVtLnRpbWUoc3ZtX3JiZl9wcmVkIDwtIHN2bV90ZXN0KHN2bV9yYWRpYWxfbW9kLCBzdm1fdGVzdGluZ19kYXRhKSkKICAgIHN2bV9hY2N1WzJdID0gbWVhbihyb3VuZChzdm1fcmJmX3ByZWQgPT0gc3ZtX3Rlc3RpbmdfZGF0YSRsYWJlbCkpCiAgICB0cHIuZnByLnJiZiA8LSBXZWlnaHRlZFJPQyhhcy5udW1lcmljKHN2bV9saW5lYXJfcHJlZCksIHN2bV90ZXN0aW5nX2RhdGEkbGFiZWwpCiAgICBzdm1fYXVjWzJdID0gV2VpZ2h0ZWRBVUModHByLmZwci5yYmYpCiAgICAKICAgIHNhdmUodG1fc3ZtX3JhZGlhbF9tb2QsIGZpbGU9Ii4uL291dHB1dC90bV9zdm1fbGluZWFyX3Rlc3QuUkRhdGEiKQogICAgCiAgICAjIyBwZXJmb3JtYW5jZQogICAgc3ZtX2F1YwogICAgc3ZtX2FjY3UKCiAgICBjYXQoIlRoZSBhY2N1cmFjeSBvZiBzdm0gbW9kZWwgaXMiLCBzdm1fYWNjdVsyXSoxMDAsICIlLlxuIikKICAgIGNhdCgiVGhlIEFVQyBvZiBzdm0gbW9kZWwgaXMiLCBzdm1fYXVjWzJdLCAiLlxuIikKICB9IGVsc2UgewogICAgbG9hZChmaWxlPSIuLi9vdXRwdXQvdG1fc3ZtX3JlYmFsYW5jZWRfdGVzdC5SRGF0YSIpCiAgfQp9IGVsc2UgewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3N2bV9yYWRpYWxfbW9kLlJEYXRhIikKICBsb2FkKGZpbGU9Ii4uL291dHB1dC90bV9zdm1fcmFkaWFsX21vZC5SRGF0YSIpCn0KYGBgCgoqIFN1bW1hcml6ZSBSdW5uaW5nIFRpbWUKCmBgYHtyfQppZihydW4uc3ZtKXsKICAjY2F0KCJUaW1lIGZvciByZWJhbGFuY2luZyB0cmFpbmluZyBkYXRhID0iLCB0bV9zdm1fcmViYWxhbmNlZF90cmFpblsxXSwgInMgXG4iKQogICNjYXQoIlRpbWUgZm9yIHJlYmFsYW5jaW5nIHRlc3RpbmcgZGF0YSA9IiwgdG1fc3ZtX3JlYmFsYW5jZWRfdGVzdFsxXSwgInMgXG4iKQogICNjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHN2bSBtb2RlbCA9IiwgdG1fc3ZtX3JhZGlhbF9tb2RbMV0sICJzIFxuIikKICBjYXQoIlRpbWUgZm9yIHRlc3Rpbmcgc3ZtIG1vZGVsPSIsIHRtX3N2bV9yYmZfdGVzdFsxXSwgInMgXG4iKQp9CmBgYAoKCiMjIFJpZGdlIE1vZGVsCgojIyMgYXBwbHkgY29uc3RydWN0ZWQgcmlkZ2UgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEKYGBge3J9CnRtX3JpZGdlX3RyYWluIDwtIE5BCmlmICh0cmFpbi5yaWRnZSl7CiAgZGF0X3RyYWluX3JlYmFsYW5jZWQgPC0gUk9TRShsYWJlbCB+IC4sIGRhdGEgPSBkYXRfdHJhaW4sIHNlZWQ9MjAyMSkkZGF0YQogIHRtX3JpZGdlX3RyYWluIDwtIHN5c3RlbS50aW1lKHJpZGdlX2N2X21vZGVsPC1yaWRnZV90cmFpbih0cmFpbl9kYXRhPWRhdF90cmFpbl9yZWJhbGFuY2VkLCBhbHBoYT1hbHBoYSwgSz1LLCBsYW1iZGE9bGFtYmRhKSkKICBzYXZlKHJpZGdlX2N2X21vZGVsLCBmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQogIHNhdmUodG1fcmlkZ2VfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9yaWRnZV90cmFpbl90aW1lLlJEYXRhIikKfWVsc2V7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX3RyYWluX3RpbWUuUkRhdGEiKQp9CmBgYAoKIyMjIHVzZSBjcm9zcy12YWxpZGF0aW9uIHRvIGNob29zZSB0aGUgb3B0aW1hbCBsYW1iZGEgd2l0aCBzbWFsbGVzdCBNU0UKYGBge3J9CmlmIChydW4uY3YpewogIHNldC5zZWVkKDIwMjApCiAgZmVhdHVyZV90cmFpbiA9IGFzLm1hdHJpeChkYXRfdHJhaW5bLCAtNjAwN10pCiAgbGFiZWxfdHJhaW4gPSBhcy5pbnRlZ2VyKGRhdF90cmFpbiRsYWJlbCkKICByaWRnZV9tb2RlbCA9IGN2LmdsbW5ldCh4PWZlYXR1cmVfdHJhaW4sIHk9bGFiZWxfdHJhaW4sIGFscGhhPWFscGhhLCBuZm9sZHM9SywgbGFtYmRhPWxhbWJkYSkKICBvcHRfbGFtYmRhID0gcmlkZ2VfbW9kZWwkbGFtYmRhLm1pbgogIHNhdmUob3B0X2xhbWJkYSwgZmlsZT0iLi4vb3V0cHV0L3JpZGdlX29wdGltYWxfbGFtYmRhLlJEYXRhIikKfWVsc2V7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvcmlkZ2Vfb3B0aW1hbF9sYW1iZGEuUkRhdGEiKQp9CmBgYAoKIyMjIHByZWRpY3QgdGVzdGluZyBkYXRhIHdpdGggdGhlIG9wdGltYWwgbGFtYmRhCmBgYHtyfQp0bV9yaWRnZV90ZXN0ID0gTkEKaWYocnVuLnRlc3QpewogIGxvYWQoIi4uL291dHB1dC9yaWRnZV9jdl9tb2RlbC5SRGF0YSIpCiAgZmVhdHVyZV90ZXN0IDwtIGFzLm1hdHJpeChkYXRfdGVzdFssIC02MDA3XSkKICB0bV9yaWRnZV90ZXN0IDwtIHN5c3RlbS50aW1lKGxhYmVsX3ByZWQ8LWFzLmludGVnZXIocmlkZ2VfdGVzdChtb2RlbD1yaWRnZV9jdl9tb2RlbCwgZmVhdHVyZXM9ZmVhdHVyZV90ZXN0LCBwcmVkLnR5cGUgPSAnY2xhc3MnKSkpCiAgc2F2ZSh0bV9yaWRnZV90ZXN0LCBmaWxlPSIuLi9vdXRwdXQvcmlkZ2VfdGVzdF90aW1lLlJEYXRhIikKfSBlbHNlewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L3JpZGdlX3Rlc3RfdGltZS5SRGF0YSIpCn0KYGBgCgojIyMgc3VtbWFyaXplIHJ1bm5pbmcgdGltZQpgYGB7cn0KY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdHJhaW5pbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90cmFpblsxXSwgInMgXG4iKQpjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0ZXN0aW5nIGZlYXR1cmVzPSIsIHRtX2ZlYXR1cmVfdGVzdFsxXSwgInMgXG4iKQpjYXQoIlRpbWUgZm9yIHRyYWluaW5nIHJpZGdlIG1vZGVsPSIsIHRtX3JpZGdlX3RyYWluWzFdLCAicyBcbiIpIApjYXQoIlRpbWUgZm9yIHRlc3RpbmcgcmlkZ2UgbW9kZWw9IiwgdG1fcmlkZ2VfdGVzdFsxXSwgInMgXG4iKQpgYGAKCiMjIyBydW4gZXZhbHVhdGlvbiBvbiBpbmRlcGVuZGVudCB0ZXN0aW5nIGRhdGEgCmBgYHtyfQpsb2FkKCIuLi9vdXRwdXQvcmlkZ2VfY3ZfbW9kZWwuUkRhdGEiKQpmZWF0dXJlX3Rlc3QgPC0gYXMubWF0cml4KGRhdF90ZXN0WywgLTYwMDddKQpsYWJlbF9wcmVkID0gYXMuaW50ZWdlcihwcmVkaWN0KHJpZGdlX2N2X21vZGVsLCBzPW9wdF9sYW1iZGEsIG5ld3g9ZmVhdHVyZV90ZXN0LCB0eXBlPSdjbGFzcycpKQpsYWJlbF90ZXN0ID0gYXMuaW50ZWdlcihkYXRfdGVzdCRsYWJlbCkKY29tcGFyZSA8LSBjYmluZCAobGFiZWxfdGVzdCwgbGFiZWxfcHJlZCkKcmlkZ2VfYWNjdXJhY3kgPSBtZWFuKGFwcGx5KGNvbXBhcmUsIDEsIG1pbikvYXBwbHkoY29tcGFyZSwgMSwgbWF4KSkgCmNhdCgiVGhlIGFjY3VyYWN5IG9mIHRoZSByaWRnZSBtb2RlbCBpcyIsIHJpZGdlX2FjY3VyYWN5KjEwMCwgIiUuXG4iKQpyaWRnZV9BVUMgPSBhdWMocm9jKGxhYmVsX3ByZWQsbGFiZWxfdGVzdCkpCmNhdCgiVGhlIEFVQyBvZiB0aGUgcmlkZ2UgbW9kZWwgaXMiLCByaWRnZV9BVUMsICIuXG4iKQpgYGAKCgojIyMgUENBICsgTERBCgpgYGB7cn0KaWYocnVuLnBjYV9sZGEpewogIGlmKG5lZWRzLmJhbGFuY2VkKXsKICAgIGJhbGFuY2VkX3RyYWluX2RhdGEgPC0gUk9TRShsYWJlbH4uLGRhdGEgPSBkYXRfdHJhaW4pJGRhdGEKICAgIHNhdmUoYmFsYW5jZWRfdHJhaW5fZGF0YSwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfYmFsYW5jZWRfdHJhaW4uUkRhdGEiKQogIH0gZWxzZSB7CiAgICBsb2FkKGJhbGFuY2VkX3RyYWluX2RhdGEsIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKICB9CiAgCiAgCiAgCiAgCiAgIyMjIHRlc3QKICBpZihydW4ucGNhX2xhZC50ZXN0KXsKICAgIGlmKG5lZWRzLmJhbGFuY2VkKXsKICAgICAgYmFsYW5jZWRfdGVzdF9kYXRhIDwtIFJPU0UobGFiZWx+LixkYXRhID0gZGF0X3Rlc3QpJGRhdGEKICAgIHNhdmUoYmFsYW5jZWRfdGVzdF9kYXRhLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9iYWxhbmNlZF90ZXN0LlJEYXRhIikKICAgIH0gZWxzZSB7CiAgICAgIGxvYWQoYmFsYW5jZWRfdGVzdF9kYXRhLCBmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV9iYWxhbmNlZF90ZXN0LlJEYXRhIikKICAgIH0KICB9Cn0KYGBgCgoKIyMjUmVmZXJlbmNlCi0gRHUsIFMuLCBUYW8sIFkuLCAmIE1hcnRpbmV6LCBBLiBNLiAoMjAxNCkuIENvbXBvdW5kIGZhY2lhbCBleHByZXNzaW9ucyBvZiBlbW90aW9uLiBQcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcywgMTExKDE1KSwgRTE0NTQtRTE0NjIu